package main

import (
	"context"
	"crypto/rand"
	"crypto/tls"
	"embed"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/fs"
	"log"
	random "math/rand"
	"net/http"
	"net/smtp"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/jordan-wright/email"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"golang.org/x/net/html"
	"gopkg.in/ini.v1"
	"gopkg.in/mgo.v2/bson"
)

//go:embed public/**
var embeddedFiles embed.FS

// 全局mongoDB
var mongoDB *mongo.Client

func init() {
	var err error
	mongoDB, err = connectMongoDB()
	if err != nil {
		log.Println(err)
	}
}

// 常量定义
const (
	defaultExpireTime  = 30 * 24 * time.Hour // token 过期时间
	defaultTokenCount  = 10                  // 最多存储的 token 数量
	dataDir            = "data"
	tokenFileName      = "tokens.json"
	navigationFileName = "navigation.json"
	configFileName     = "config.ini"
)

// 环境变量
var (
	apiPort             string // API服务端口
	webPort             string // 静态文件服务端口
	envUsername         string // 登录用户名
	envPassword         string // 登录密码
	envEnableNoAuth     bool   // 是否启用无用户密码模式
	envEnableNoAuthView bool   // 是否启用无用户密码浏览模式

	// MongoDB相关环境变量
	envMongoURI      string // MongoDB连接URI
	envMongoDBName   string // MongoDB数据库名
	envMongoSync     bool   // 是否启用MongoDB同步
	envMongoInterval int    // 同步间隔(分钟)
)

// MongoDB配置
const (
	defaultMongoURI        = "mongodb://localhost:27017"
	defaultMongoDBName     = "navigation_db"
	defaultMongoCollection = "navigation_data"
	defaultMongoSync       = true
	defaultMongoInterval   = 1 // 同步间隔，分钟
)

// MongoDB文档结构
type NavigationDocument struct {
	ID           string     `json:"id" bson:"_id,omitempty"`
	UserID       string     `json:"userId" bson:"userId"` // 新增用户标识字段
	Data         Navigation `json:"data" bson:"data"`
	LastModified int64      `json:"lastModified" bson:"lastModified"`
	CreatedAt    time.Time  `json:"createdAt" bson:"createdAt"`
}

// =========================
// 模块: 用户认证
// =========================

// 用户信息
type User struct {
	Username string `json:"username"`
	Password string `json:"password"`
	Email    string `json:"email"` // 新增邮箱字段
}

// 验证码存储结构
type VerificationCode struct {
	Email    string
	Code     string
	ExpireAt time.Time
}

// MongoDB 用户文档结构
type UserDocument struct {
	Email     string    `json:"email" bson:"email"`
	Password  string    `json:"password" bson:"password"`
	CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
}

// 验证码存储
var verificationCodes = make(map[string]VerificationCode)
var verificationCodesMutex sync.Mutex

// 生成随机验证码
func generateVerificationCode() string {
	random.New(random.NewSource(time.Now().UnixNano()))
	res := ""
	for i := 0; i < 6; i++ {
		res += strconv.Itoa(random.Intn(10))
	}
	return res
}

// 发送邮箱验证码
func sendVerificationEmail(toUserEmail string, code string) error {
	e := email.NewEmail()

	e.From = "1690544550@qq.com"
	e.To = []string{toUserEmail}
	e.Subject = "Your validate code"
	e.HTML = []byte(`
	<!DOCTYPE html>
	<html lang="en">
	<head>
	    <meta charset="UTF-8">
	    <title>Verification Code</title>
	</head>
	<body>
	    <h1>Go-TinyNav | 你的验证码为: ` + code + `</h1>
	</body>
	</html>
	`)

	// 设置 TLS 配置
	tlsConfig := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         "smtp.qq.com",
	}

	// 构造 SMTP 服务器地址
	smtpAddr := fmt.Sprintf("%s:%d", "smtp.qq.com", 465)

	// 发送邮件
	err := e.SendWithTLS(smtpAddr, smtp.PlainAuth("",
		"1690544550@qq.com",
		"znxogkkkexpbeeaj",
		"smtp.qq.com",
	), tlsConfig)
	if err != nil {
		// just ignore it
		log.Println("Failed to send email: ", err)
		// todo: bug 不返回错误，继续执行后续逻辑
		return nil
	}

	return nil
}

// 发送验证码接口
func sendVerificationCodeHandler(c *gin.Context) {
	var request struct {
		Email string `json:"email"`
	}
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
		return
	}

	code := generateVerificationCode()
	expireAt := time.Now().Add(5 * time.Minute) // 验证码 5 分钟过期

	verificationCodesMutex.Lock()
	verificationCodes[request.Email] = VerificationCode{
		Email:    request.Email,
		Code:     code,
		ExpireAt: expireAt,
	}
	verificationCodesMutex.Unlock()

	if err := sendVerificationEmail(request.Email, code); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send verification email"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "Verification code sent successfully"})
}

// 邮箱注册接口
func registerWithEmailHandler(c *gin.Context) {
	var request struct {
		Email    string `json:"email"`
		Code     string `json:"code"`
		Password string `json:"password"`
	}
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
		return
	}

	// 验证验证码
	verificationCodesMutex.Lock()
	verificationCode, exists := verificationCodes[request.Email]
	verificationCodesMutex.Unlock()

	if !exists || time.Now().After(verificationCode.ExpireAt) || verificationCode.Code != request.Code {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid verification code"})
		return
	}

	// **新增：检查邮箱是否已注册**
	userExists, err := checkEmailExists(request.Email)
	if err != nil {
		c.JSON(
			http.StatusInternalServerError,
			gin.H{"error": "Failed to check user existence", "detail": err.Error()},
		)
		return
	}
	if userExists {
		c.JSON(http.StatusConflict, gin.H{"error": "Email already registered"})
		return
	}

	// 准备用户文档
	userDoc := UserDocument{
		Email:     request.Email,
		Password:  request.Password,
		CreatedAt: time.Now(),
	}

	// 插入用户到 MongoDB
	if err := insertUserToMongoDB(userDoc); err != nil {
		c.JSON(
			http.StatusInternalServerError,
			gin.H{"error": "Failed to register user", "detail": err.Error()},
		)
		return
	}

	// 生成新的令牌
	token, err := generateToken()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
		return
	}
	c.Header("Authorization", token)
	c.JSON(http.StatusOK, gin.H{"token": token})
}

// 检查邮箱是否已注册
func checkEmailExists(email string) (bool, error) {
	var count int64
	filter := bson.M{"email": email}
	// 连接 MongoDB
	client, err := connectMongoDB()
	if err != nil {
		return false, err
	}
	defer client.Disconnect(context.Background())
	collection := client.Database(envMongoDBName).Collection("users")
	if _, err := collection.CountDocuments(context.Background(), filter, nil); err != nil {
		return false, err
	}
	return count > 0, nil
}

// 将用户插入 MongoDB
func insertUserToMongoDB(user UserDocument) error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 连接 MongoDB
	client, err := connectMongoDB()
	if err != nil {
		return err
	}
	defer client.Disconnect(ctx)

	// 获取用户集合
	collection := client.Database(envMongoDBName).Collection("users")

	// 插入用户文档
	_, err = collection.InsertOne(ctx, user)
	if err != nil {
		return fmt.Errorf("failed to insert user: %w", err)
	}

	return nil
}

// Token结构体
type Token struct {
	Value    string    `json:"value"`    // 添加JSON标签确保序列化
	UserID   string    `json:"userId"`   // 添加JSON标签确保序列化
	ExpireAt time.Time `json:"expireAt"` // 添加JSON标签确保序列化
}

// Token存储结构
type TokenStore struct {
	tokens   map[string]Token
	mu       sync.Mutex
	filePath string
}

// 创建新的TokenStore
func newTokenStore() *TokenStore {
	// 确保data目录存在
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		log.Printf("Failed to create data directory: %v", err)
	}

	ts := &TokenStore{
		tokens:   make(map[string]Token),
		filePath: filepath.Join(dataDir, tokenFileName),
	}

	// 从文件加载现有token
	ts.loadTokens()

	return ts
}

// 保存tokens到文件
func (ts *TokenStore) saveTokens() error {

	log.Println("开始将 tokens 转换为可序列化的格式")
	// 将tokens转换为可序列化的格式
	data, err := json.MarshalIndent(ts.tokens, "", "  ")
	if err != nil {
		log.Printf("序列化 tokens 时出错: %v", err)
		return fmt.Errorf("error marshaling tokens: %v", err)
	}
	log.Println("tokens 序列化完成")

	log.Printf("开始将数据写入文件: %s", ts.filePath)
	// 写入文件
	err = os.WriteFile(ts.filePath, data, 0644)
	if err != nil {
		log.Printf("写入 tokens 文件时出错: %v", err)
		return fmt.Errorf("error writing tokens file: %v", err)
	}
	log.Printf("数据成功写入文件: %s", ts.filePath)

	return nil
}

// 从文件加载tokens
func (ts *TokenStore) loadTokens() {
	ts.mu.Lock()
	defer ts.mu.Unlock()

	data, err := os.ReadFile(ts.filePath)
	if err != nil {
		if !os.IsNotExist(err) {
			log.Printf("Error reading tokens file: %v", err)
		}
		return
	}

	err = json.Unmarshal(data, &ts.tokens)
	if err != nil {
		log.Printf("Error unmarshaling tokens: %v", err)
		ts.tokens = make(map[string]Token) // 如果出错，使用空map
	}

	// 清理已过期的token
	now := time.Now()
	for k, v := range ts.tokens {
		if now.After(v.ExpireAt) {
			delete(ts.tokens, k)
		}
	}
}

// 添加新token
func (ts *TokenStore) addToken(token string, userID string, duration time.Duration) {
	ts.mu.Lock()
	defer ts.mu.Unlock()
	log.Println("添加token时打印userId: ", userID)

	ts.tokens[token] = Token{
		Value:    token,
		UserID:   userID,
		ExpireAt: time.Now().Add(duration),
	}

	// 统一使用saveTokens方法保存
	if err := ts.saveTokens(); err != nil {
		log.Printf("Failed to save tokens: %v", err)
	}
}

// 从令牌获取用户信息
func getUserIDFromToken(tokenStore *TokenStore, token string) (string, bool) {
	tokenStore.mu.Lock()
	defer tokenStore.mu.Unlock()

	log.Printf("getUserIDFromToken: checking token %s", token)

	t, exists := tokenStore.tokens[token]
	if !exists || time.Now().After(t.ExpireAt) {
		log.Printf("getUserIDFromToken: token %s not found or expired", token)
		return "", false
	}

	log.Printf("getUserIDFromToken: found token for user %s", t.UserID)
	return t.UserID, true
}

// 验证token并返回用户ID
func (ts *TokenStore) validateToken(token string) (string, bool) {
	ts.mu.Lock()
	defer ts.mu.Unlock()

	t, exists := ts.tokens[token]
	if !exists {
		return "", false
	}
	if time.Now().After(t.ExpireAt) {
		delete(ts.tokens, token)
		return "", false
	}
	newExpireAt := time.Now().Add(defaultExpireTime)
	ts.tokens[token] = Token{Value: token, UserID: t.UserID, ExpireAt: newExpireAt}
	// 保存到文件
	if err := ts.saveTokens(); err != nil {
		log.Printf("Error saving tokens: %v", err)
	}
	return t.UserID, true
}

// 生成随机令牌
func generateToken() (string, error) {
	b := make([]byte, 32)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(b), nil
}

// 认证中间件
func authMiddleware(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		token := c.GetHeader("Authorization")
		userID, isValid := tokenStore.validateToken(token)
		if !isValid {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			c.Abort()
			return
		}
		// 将用户ID存储到请求上下文中
		c.Set("userID", userID)
		c.Next()
	}
}

// 登录请求结构体
type LoginRequest struct {
	Username string `json:"username"` // 可以是用户名或邮箱
	Password string `json:"password"`
}

// 统一登录处理
func loginHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req LoginRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
			return
		}

		var userID string
		authCheck := false
		// 无认证模式
		if envEnableNoAuth {
			log.Println("No authentication required (ENABLE_NO_AUTH=true)")
			authCheck = true
			userID = "no-auth-user" // 无认证用户的用户 ID
			log.Printf("No auth mode, userID set to: %s", userID)
		} else {
			// 检查是否为邮箱格式
			if strings.Contains(req.Username, "@") {
				// 邮箱认证
				if checkEmailAuth(req.Username, req.Password) {
					authCheck = true
					userID = req.Username // 使用邮箱作为用户 ID
					log.Printf("Email auth success, userID set to: %s", userID)
				} else {
					log.Printf("Email auth failed for: %s", req.Username)
				}
			} else {
				// 用户名密码认证
				if req.Username == envUsername && req.Password == envPassword {
					authCheck = true
					userID = req.Username // 使用用户名作为用户 ID
					log.Printf("Username auth success, userID set to: %s", userID)
				} else {
					log.Printf("Username auth failed for: %s", req.Username)
				}
			}
		}

		if !authCheck {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		// 生成新的令牌
		token, err := generateToken()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
			return
		}
		// 正确传递 userID 到 addToken 方法
		log.Printf("Adding token with userID: %s", userID)
		tokenStore.addToken(token, userID, defaultExpireTime)
		c.Header("Authorization", token)
		c.JSON(http.StatusOK, gin.H{"token": token})
	}
}

// 检查邮箱认证
func checkEmailAuth(email, password string) bool {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	client, err := connectMongoDB()
	if err != nil {
		log.Printf("Failed to connect to MongoDB: %v", err)
		return false
	}
	defer client.Disconnect(ctx)

	collection := client.Database(envMongoDBName).Collection("users")
	var user UserDocument
	err = collection.FindOne(ctx, bson.M{"email": email, "password": password}).Decode(&user)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			log.Printf("User not found for email: %s", email)
		} else {
			log.Printf("Error querying user: %v", err)
		}
		return false
	}
	log.Printf("User found for email: %s", email)
	return true
}

// =========================
// 模块: 导航管理
// =========================

// 链接信息
type Link struct {
	Name      string `json:"name"`
	Url       string `json:"url"`
	Icon      string `json:"icon"`
	Category  string `json:"category"`
	SortIndex int    `json:"sortIndex"`
}

// 导航结构
type Navigation struct {
	Links        []Link   `json:"links"`
	Categories   []string `json:"categories"`
	LastModified int64    `json:"lastModified"`
}

// 导航最后修改时间
type NavigationLastModified struct {
	LastModified int64 `json:"lastModified"`
}

// 配置信息
type Config struct {
	EnableNoAuth     bool `json:"enableNoAuth"`
	EnableNoAuthView bool `json:"enableNoAuthView"`
}

// 更新分类列表
func updateCategories(nav *Navigation) {
	// 创建当前链接中存在的分类集合
	currentCategories := make(map[string]struct{})
	for _, link := range nav.Links {
		if link.Category != "" {
			currentCategories[link.Category] = struct{}{}
		}
	}

	// 如果 Categories 为空，初始化它
	if nav.Categories == nil {
		nav.Categories = make([]string, 0)
	}

	// 删除不存在的分类（保持原有顺序）
	newCategories := make([]string, 0, len(nav.Categories))
	for _, category := range nav.Categories {
		if _, exists := currentCategories[category]; exists {
			newCategories = append(newCategories, category)
			delete(currentCategories, category) // 从当前分类集合中删除已处理的分类
		}
	}

	// 添加新的分类（将剩余的分类追加到列表末尾）
	for category := range currentCategories {
		newCategories = append(newCategories, category)
	}

	nav.Categories = newCategories
}

// 加载导航数据
func loadNavigation(userID string) (Navigation, error) {
	// 确保 data 目录存在
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		return Navigation{}, fmt.Errorf("failed to create data directory: %v", err)
	}

	navPath := filepath.Join(dataDir, fmt.Sprintf("%s_%s", userID, navigationFileName))
	data, err := os.ReadFile(navPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return Navigation{}, err
		}
		return Navigation{}, nil
	}
	var nav Navigation
	err = json.Unmarshal(data, &nav)
	if err != nil {
		return Navigation{}, err
	}
	return nav, nil
}

// 保存导航数据
func saveNavigation(userID string, nav Navigation) error {
	// 确保 data 目录存在
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		return fmt.Errorf("failed to create data directory: %v", err)
	}

	currentTime := time.Now()
	lastModified := currentTime.UnixNano() / int64(time.Millisecond)
	nav.LastModified = lastModified

	data, err := json.MarshalIndent(nav, "", "  ")
	if err != nil {
		return err
	}

	navPath := filepath.Join(dataDir, fmt.Sprintf("%s_%s", userID, navigationFileName))
	return os.WriteFile(navPath, data, 0644)
}

// 获取导航数据
func getNavigationHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求上下文中获取用户ID
		userID, exists := c.Get("userID")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User ID not found"})
			return
		}

		nav, err := loadNavigation(userID.(string))
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}
		c.JSON(http.StatusOK, nav)
	}
}

// 获取导航最后修改时间
// 确保函数接收 tokenStore 作为参数
func getNavigationLastModifiedHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求头获取 token
		token := c.GetHeader("Authorization")
		// 从 token 获取用户 ID
		userID, ok := getUserIDFromToken(tokenStore, token)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		// 传入用户 ID 加载导航数据
		nav, err := loadNavigation(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}
		c.JSON(http.StatusOK, NavigationLastModified{LastModified: nav.LastModified})
	}
}

// 添加链接
func addLinkHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求上下文中获取用户ID
		userID, exists := c.Get("userID")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User ID not found"})
			return
		}

		var newLink Link
		if err := c.ShouldBindJSON(&newLink); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid link data"})
			return
		}
		nav, err := loadNavigation(userID.(string))
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}
		nav.Links = append(nav.Links, newLink)
		updateCategories(&nav)
		if err := saveNavigation(userID.(string), nav); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save navigation"})
			return
		}
		c.Status(http.StatusOK)
	}
}

// 更新链接
// 确保函数接收 tokenStore 作为参数
func updateLinkHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		var updatedLink Link
		if err := c.ShouldBindJSON(&updatedLink); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid link data"})
			return
		}

		index := c.Param("id")
		idx, err := stringToInt(index)
		if err != nil || idx < 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid index"})
			return
		}

		// 从请求头获取 token
		token := c.GetHeader("Authorization")
		// 从 token 获取用户 ID
		userID, ok := getUserIDFromToken(tokenStore, token)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		// 传入用户 ID 加载导航数据
		nav, err := loadNavigation(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}

		if idx >= len(nav.Links) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Index out of range"})
			return
		}

		nav.Links[idx] = updatedLink
		updateCategories(&nav)

		// 传入用户 ID 保存导航数据
		if err := saveNavigation(userID, nav); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save navigation"})
			return
		}

		c.Status(http.StatusOK)
	}
}

// 删除链接
func deleteLinkHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 从请求头获取 token
		token := c.GetHeader("Authorization")
		// 从 token 获取用户 ID
		userID, ok := getUserIDFromToken(tokenStore, token)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		index := c.Param("id")
		idx, err := stringToInt(index)
		if err != nil || idx < 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid index"})
			return
		}

		// 传入用户 ID 加载导航数据
		nav, err := loadNavigation(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}
		if idx >= len(nav.Links) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Index out of range"})
			return
		}
		nav.Links = append(nav.Links[:idx], nav.Links[idx+1:]...)
		updateCategories(&nav)
		// 传入用户 ID 保存导航数据
		if err := saveNavigation(userID, nav); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save navigation"})
			return
		}
		c.Status(http.StatusOK)
	}
}

// 更新排序索引
type UpdateSortIndexRequest struct {
	Updates []struct {
		Index     int    `json:"index"`              // 链接在数组中的索引
		SortIndex int    `json:"sortIndex"`          // 新的排序索引值
		Category  string `json:"category,omitempty"` // 可选的分类更新
	} `json:"updates"`
}

// 确保函数接收 tokenStore 作为参数
func updateSortIndicesHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req UpdateSortIndexRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
			return
		}

		// 从请求头获取 token
		token := c.GetHeader("Authorization")
		// 从 token 获取用户 ID
		userID, ok := getUserIDFromToken(tokenStore, token)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		// 传入用户 ID 加载导航数据
		nav, err := loadNavigation(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}

		// 批量更新 sortIndex 和 category
		needUpdateCategories := false
		for _, update := range req.Updates {
			if update.Index < 0 || update.Index >= len(nav.Links) {
				c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Invalid index: %d", update.Index)})
				return
			}
			nav.Links[update.Index].SortIndex = update.SortIndex
			if update.Category != "" {
				nav.Links[update.Index].Category = update.Category
				needUpdateCategories = true
			}
		}

		// 更新分类列表
		if needUpdateCategories {
			updateCategories(&nav)
		}

		// 传入用户 ID 保存导航数据
		if err := saveNavigation(userID, nav); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save navigation"})
			return
		}

		c.Status(http.StatusOK)
	}
}

// 更新分类
type UpdateCategorysRequest struct {
	Categories []string `json:"categories"`
}

// 确保函数接收 tokenStore 作为参数
func updateCategoriesHandler(tokenStore *TokenStore) gin.HandlerFunc {
	return func(c *gin.Context) {
		var req UpdateCategorysRequest
		if err := c.ShouldBindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
			return
		}

		// 从请求头获取 token
		token := c.GetHeader("Authorization")
		// 从 token 获取用户 ID
		userID, ok := getUserIDFromToken(tokenStore, token)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
			return
		}

		// 传入用户 ID 加载导航数据
		nav, err := loadNavigation(userID)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load navigation"})
			return
		}

		// 获取当前所有实际使用的分类
		currentCategories := make(map[string]struct{})
		for _, link := range nav.Links {
			if link.Category != "" {
				currentCategories[link.Category] = struct{}{}
			}
		}

		// 验证新的分类列表包含所有正在使用的分类
		for category := range currentCategories {
			found := false
			for _, newCategory := range req.Categories {
				if category == newCategory {
					found = true
					break
				}
			}
			if !found {
				c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("Cannot remove category '%s' that is still in use", category)})
				return
			}
		}

		// 更新分类列表
		nav.Categories = req.Categories

		// 传入用户 ID 保存更新后的导航数据
		if err := saveNavigation(userID, nav); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save navigation"})
			return
		}

		// 返回更新后的完整导航数据
		c.JSON(http.StatusOK, nav)
	}
}

// 获取配置
func getConfigHandler(c *gin.Context) {
	config := Config{
		EnableNoAuth:     envEnableNoAuth,
		EnableNoAuthView: envEnableNoAuthView,
	}
	c.JSON(http.StatusOK, config)
}

// 辅助函数：字符串转整数
func stringToInt(s string) (int, error) {
	var i int
	_, err := fmt.Sscanf(s, "%d", &i)
	return i, err
}

// =========================
// 模块: 图标获取
// =========================

// 获取图标
func getIconHandler(c *gin.Context) {
	urlParam := c.Query("url")
	if urlParam == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing URL parameter"})
		return
	}

	parsedURL, err := url.Parse(urlParam)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid URL"})
		return
	}

	iconURL := fmt.Sprintf("%s://%s/favicon.ico", parsedURL.Scheme, parsedURL.Host)
	// 创建HTTP客户端和请求
	tr := &http.Transport{
		TLSHandshakeTimeout: 10 * time.Second,
		DisableKeepAlives:   true,
		ForceAttemptHTTP2:   true,
	}
	client := &http.Client{Transport: tr}
	req, err := http.NewRequest("GET", iconURL, nil)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error creating request: %v", err)})
		return
	}

	// 设置请求头
	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")

	// 执行HTTP GET请求
	resp, err := client.Do(req)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Error performing GET request: %v", err)})
		return
	}
	defer resp.Body.Close()

	finalURL := resp.Request.URL.String() // 获取跳转后的真实 URL
	log.Printf("Fetched icon from: %s", finalURL)

	contentType := resp.Header.Get("Content-Type")
	if resp.StatusCode != http.StatusOK || !strings.HasPrefix(contentType, "image/") {
		// fallback：从 HTML 中获取图标
		log.Printf("Fallback to HTML icon parsing, code:%d Content-Type:%s", resp.StatusCode, contentType)
		// 尝试解析 HTML 来获取图标
		iconURL, err = fetchIconFromHTML(urlParam)
		if err != nil {
			log.Printf("Failed to fetch icon: %v", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch icon"})
			return
		}

		log.Printf("Fetching icon from HTML URL: %s", iconURL)
		req, _ = http.NewRequest("GET", iconURL, nil)
		req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0...)")
		req.Header.Set("Accept", "image/*,*/*;q=0.8")
		resp, err = client.Do(req)
		if err != nil || resp.StatusCode != http.StatusOK {
			log.Printf("Failed to fetch icon from HTML: %v", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch icon"})
			return
		}
		defer resp.Body.Close()
		contentType = resp.Header.Get("Content-Type")
		if contentType == "" {
			contentType = "image/x-icon"
		}
	}

	// 读取图标数据
	iconData, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read icon data: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read icon data"})
		return
	}

	// 将图标数据转换为base64编码
	base64Data := base64.StdEncoding.EncodeToString(iconData)

	// 返回base64编码的图标数据
	c.JSON(http.StatusOK, gin.H{
		"iconData": fmt.Sprintf("data:%s;base64,%s", contentType, base64Data),
	})
}

// 从HTML中获取图标
func fetchIconFromHTML(pageURL string) (string, error) {
	resp, err := http.Get(pageURL)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("failed to fetch page, status code: %d. pageURL:%s", resp.StatusCode, pageURL)
	}

	doc, err := html.Parse(resp.Body)
	if err != nil {
		return "", err
	}

	var iconURL string
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "link" {
			for _, attr := range n.Attr {
				if attr.Key == "rel" && (attr.Val == "icon" || attr.Val == "shortcut icon") {
					for _, attr := range n.Attr {
						if attr.Key == "href" {
							iconURL = attr.Val
							log.Printf("Icon URL found: %s", iconURL)
							if !strings.HasPrefix(iconURL, "http") && !strings.HasPrefix(iconURL, "//") {
								baseURL, _ := url.Parse(pageURL)
								iconURL = baseURL.ResolveReference(&url.URL{Path: iconURL}).String()
							}
							if strings.HasPrefix(iconURL, "//") {
								baseURL, _ := url.Parse(pageURL)
								iconURL = baseURL.Scheme + ":" + iconURL
							}
							return
						}
					}
				}
			}
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)

	if iconURL == "" {
		return "", fmt.Errorf("icon not found in HTML")
	}

	return iconURL, nil
}

// =========================
// 模块: 主程序
// =========================

// 加载配置，移除读取环境变量的代码，让程序仅从配置文件和命令行参数获取配置，避免环境变量易变和不明确的问题。
// 调整配置优先级，命令行参数优先级高于配置文件，保证配置的灵活性。
func loadConfig() {
	// 解析命令行参数
	apiPortPtr := flag.String("api-port", "58080", "Port for API server")
	webPortPtr := flag.String("web-port", "58081", "Port for web server")
	user := flag.String("user", "", "Username for authentication")
	password := flag.String("password", "", "Password for authentication")
	noAuth := flag.Bool("no-auth", false, "Enable no-auth mode")
	noAuthView := flag.Bool("no-auth-view", false, "Enable no-auth-view mode")

	// MongoDB相关命令行参数
	mongoURIPtr := flag.String("mongo-uri", defaultMongoURI, "MongoDB connection URI (default: \"mongodb://localhost:27017\")")
	mongoDBNamePtr := flag.String("mongo-db", defaultMongoDBName, "MongoDB database name (default: \"navigation_db\")")
	mongoSyncPtr := flag.Bool("mongo-sync", defaultMongoSync, "Enable MongoDB synchronization (default: true)")
	mongoIntervalPtr := flag.Int("mongo-interval", defaultMongoInterval, "MongoDB synchronization interval in minutes (default: 1)")

	flag.Parse()

	// 确保 data 目录存在
	if err := os.MkdirAll(dataDir, 0755); err != nil {
		fmt.Printf("failed to create data directory: %v", err)
	}

	configPath := filepath.Join(dataDir, configFileName)

	cfg, err := ini.Load(configPath)
	if err != nil {
		log.Printf("Failed to read config file: %v", err)
	}

	// 优先级：命令行参数 > 配置文件
	envUsername = *user
	if envUsername == "" && cfg != nil {
		envUsername = cfg.Section("").Key("NAV_USERNAME").String()
	}

	envPassword = *password
	if envPassword == "" && cfg != nil {
		envPassword = cfg.Section("").Key("NAV_PASSWORD").String()
	}

	envEnableNoAuth = *noAuth
	if !envEnableNoAuth && cfg != nil {
		envEnableNoAuth = cfg.Section("").Key("ENABLE_NO_AUTH").MustBool(false)
	}

	envEnableNoAuthView = *noAuthView
	if !envEnableNoAuthView && cfg != nil {
		envEnableNoAuthView = cfg.Section("").Key("ENABLE_NO_AUTH_VIEW").MustBool(false)
	}

	// MongoDB相关配置
	envMongoURI = *mongoURIPtr
	if envMongoURI == defaultMongoURI && cfg != nil {
		envMongoURI = cfg.Section("").Key("MONGODB_URI").MustString(defaultMongoURI)
	}

	envMongoDBName = *mongoDBNamePtr
	if envMongoDBName == defaultMongoDBName && cfg != nil {
		envMongoDBName = cfg.Section("").Key("MONGODB_DB").MustString(defaultMongoDBName)
	}

	envMongoSync = *mongoSyncPtr
	if !envMongoSync && cfg != nil {
		envMongoSync = cfg.Section("").Key("MONGODB_SYNC").MustBool(defaultMongoSync)
	}

	envMongoInterval = *mongoIntervalPtr
	if envMongoInterval == defaultMongoInterval && cfg != nil {
		envMongoInterval = cfg.Section("").Key("MONGODB_INTERVAL").MustInt(defaultMongoInterval)
	}
	// 确保间隔大于0
	if envMongoInterval <= 0 {
		envMongoInterval = defaultMongoInterval
	}

	// 解引用指针获取实际的字符串值
	apiPort = *apiPortPtr
	webPort = *webPortPtr

	log.Printf("加载到的配置文件: "+
		"API_PORT=%s, WEB_PORT=%s, "+
		"NAV_USERNAME=%s, ENABLE_NO_AUTH=%v, ENABLE_NO_AUTH_VIEW=%v, "+
		"MONGODB_URI=%s, MONGODB_DB=%s, MONGODB_SYNC=%v, MONGODB_INTERVAL=%dmin",
		apiPort, webPort,
		envUsername, envEnableNoAuth, envEnableNoAuthView,
		envMongoURI, envMongoDBName, envMongoSync, envMongoInterval)
}

func main() {
	gin.SetMode(gin.ReleaseMode)
	// 添加使用说明
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "  %s [options]\n\n", os.Args[0])
		fmt.Fprintf(os.Stderr, "Options:\n")
		flag.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nExample:\n")
		fmt.Fprintf(os.Stderr, "  %s --api-port=58080 --web-port=58081 --user=admin --password=123456 --mongo-uri=mongodb://user:pass@localhost:27017/navigation --mongo-interval=5\n", os.Args[0])
	}

	loadConfig()
	tokenStore := newTokenStore()

	var mongoClient *mongo.Client
	var err error
	if envMongoSync {
		// 连接MongoDB
		mongoClient, err = connectMongoDB()
		if err != nil {
			log.Printf("Failed to connect to MongoDB: %v", err)
			log.Println("MongoDB synchronization will be disabled")
			mongoClient = nil
		} else {
			// 启动时立即同步一次
			syncAllUsersNavigation(mongoClient, tokenStore)

			// 设置定时任务
			syncDuration := time.Duration(envMongoInterval) * time.Minute
			ticker := time.NewTicker(syncDuration)
			go func() {
				for range ticker.C {
					if mongoClient != nil {
						syncAllUsersNavigation(mongoClient, tokenStore)
					}
				}
			}()
		}
	} else {
		log.Println("MongoDB synchronization is disabled")
		mongoClient = nil
	}

	// 创建API服务
	api := gin.Default()

	// 配置CORS中间件
	api.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Accept", "Authorization"},
		ExposeHeaders:    []string{"Authorization"},
		AllowCredentials: true,
		MaxAge:           86400,
	}))

	// API路由
	apiGroup := api.Group("/api")
	{
		apiGroup.GET("/navigation/:email", getUserNavigationFile)
		apiGroup.POST("/login", loginHandler(tokenStore))
		apiGroup.POST("/send-verification-code", sendVerificationCodeHandler) // 新增发送验证码接口
		apiGroup.POST("/register-with-email", registerWithEmailHandler)       // 新增邮箱注册接口
		apiGroup.GET("/navigation/last-modified", getNavigationLastModifiedHandler(tokenStore))
		// 无需认证的路由
		if envEnableNoAuthView {
			// 传入 tokenStore 实例
			apiGroup.GET("/navigation", getNavigationHandler(tokenStore))
		} else {
			// 需要认证的路由组
			authGroup := apiGroup.Group("", authMiddleware(tokenStore))
			// 传入 tokenStore 实例
			authGroup.GET("/navigation", getNavigationHandler(tokenStore))
		}

		// 需要认证的路由组
		authGroup := apiGroup.Group("", authMiddleware(tokenStore))
		{
			// 传入 tokenStore 实例
			authGroup.POST("/navigation/add", addLinkHandler(tokenStore))
			authGroup.PUT("/navigation/update/:id", updateLinkHandler(tokenStore))
			authGroup.DELETE("/navigation/delete/:id", deleteLinkHandler(tokenStore))
			authGroup.PUT("/navigation/sort", updateSortIndicesHandler(tokenStore))
			authGroup.PUT("/navigation/categories", updateCategoriesHandler(tokenStore))
			authGroup.GET("/get-icon", getIconHandler)
			authGroup.GET("/config", getConfigHandler)
			authGroup.GET("/validate", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{"status": "ok"})
			})
		}
	}

	// 创建Web服务
	web := gin.Default()

	// 静态文件服务
	publicFiles, err := fs.Sub(embeddedFiles, "public")
	if err != nil {
		log.Fatalf("Failed to load public files: %v", err)
	}
	web.StaticFS("/", http.FS(publicFiles))

	// 启动API服务 - 在独立的goroutine中运行
	go func() {
		log.Printf("API server is running on http://localhost:%s\n", apiPort)
		if err := api.Run(":" + apiPort); err != nil {
			log.Fatalf("API server failed to start: %v", err)
		}
	}()

	// 启动Web服务 - 在主goroutine中运行
	log.Printf("Web server is running on http://localhost:%s\n", webPort)
	if err := web.Run(":" + webPort); err != nil {
		log.Fatalf("Web server failed to start: %v", err)
	}
}

// 连接MongoDB
func connectMongoDB() (*mongo.Client, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	client, err := mongo.Connect(ctx, options.Client().ApplyURI(envMongoURI))
	if err != nil {
		return nil, err
	}

	// 检查连接
	err = client.Ping(ctx, nil)
	if err != nil {
		return nil, err
	}

	log.Printf("Connected to MongoDB: %s", envMongoURI)
	return client, nil
}

// 同步导航数据到MongoDB并发送通知邮件
func syncNavigationToMongoDB(client *mongo.Client, userID string) error {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 加载导航数据
	nav, err := loadNavigation(userID)
	if err != nil {
		return fmt.Errorf("failed to load navigation data for user %s: %v", userID, err)
	}

	// 准备文档
	doc := NavigationDocument{
		UserID:       userID,
		Data:         nav,
		LastModified: nav.LastModified,
		CreatedAt:    time.Now(),
	}

	// 获取集合
	collection := client.Database(envMongoDBName).Collection(defaultMongoCollection)

	// 插入或更新文档
	filter := bson.M{"userId": userID}
	update := bson.M{"$set": doc}
	opts := options.Update().SetUpsert(true)
	_, err = collection.UpdateOne(ctx, filter, update, opts)
	if err != nil {
		return fmt.Errorf("failed to sync navigation data for user %s: %v", userID, err)
	}

	log.Printf("Successfully synced navigation data for user %s to MongoDB at %s",
		userID, time.Now().Format(time.RFC3339))

	// todo: 如果你不需要邮件通知，可以注释掉
	// 新增：判断用户ID是否为邮箱格式，如果是则发送通知邮件
	if isEmail(userID) {
		// 提取用户名（邮箱前缀）
		userName := extractUserName(userID)

		// 异步发送邮件，避免阻塞主流程
		go func() {
			if err := sendNotificationEmail(userID, userName); err != nil {
				log.Printf("Failed to send notification email to %s: %v", userID, err)
			} else {
				log.Printf("Notification email sent to %s", userID)
			}
		}()
	} else {
		log.Printf("User ID %s is not an email address, skipping notification", userID)
	}

	return nil
}

// 判断字符串是否为邮箱格式
func isEmail(s string) bool {
	// 简单的邮箱格式验证，生产环境建议使用正则表达式
	return strings.Contains(s, "@") && strings.Contains(s, ".")
}

// 从邮箱地址提取用户名（前缀）
func extractUserName(email string) string {
	if idx := strings.Index(email, "@"); idx > 0 {
		return email[:idx]
	}
	return email // 如果格式不正确，返回完整字符串
}

// 同步所有用户的导航数据
func syncAllUsersNavigation(client *mongo.Client, tokenStore *TokenStore) {
	tokenStore.mu.Lock()
	defer tokenStore.mu.Unlock()

	// 存储已经同步过的用户 ID，避免重复同步
	syncedUserIDs := make(map[string]struct{})

	for _, token := range tokenStore.tokens {
		userID := token.UserID
		if _, exists := syncedUserIDs[userID]; !exists {
			if err := syncNavigationToMongoDB(client, userID); err != nil {
				log.Printf("MongoDB sync failed for user %s: %v", userID, err)
			}
			syncedUserIDs[userID] = struct{}{}
		}
	}
}
func sendNotificationEmail(toUserEmail, userName string) error {
	e := email.NewEmail()

	// 构建包含下载链接的HTML邮件内容
	emailBody := fmt.Sprintf(`
	<!DOCTYPE html>
	<html lang="zh-CN">
	<head>
	    <meta charset="UTF-8">
	    <title>Go-TinyNav 导航数据下载</title>
	</head>
	<body>
	    <h1>Go-TinyNav 导航数据同步通知</h1>
	    <p>尊敬的 %s：</p>
	    <p>您的导航数据已成功同步到服务器。</p>
	    <p>您可以通过以下链接下载您的导航JSON文件：</p>
	    <p>地址: 后端URL/api/navigation/用户邮箱?password=您的密码</p>
	    <p class="note">请妥善保管您的密码，此链接包含敏感信息。</p>
	    <p>感谢您使用 Go-TinyNav。</p>
	</body>
	</html>
	`, userName,
	//  apiPort, url.QueryEscape(toUserEmail)
	)

	e.From = "1690544550@qq.com"
	e.To = []string{toUserEmail}
	e.Subject = "Go-TinyNav 导航数据同步通知"
	e.HTML = []byte(emailBody)

	// 设置 TLS 配置
	tlsConfig := &tls.Config{
		InsecureSkipVerify: true,
		ServerName:         "smtp.qq.com",
	}

	// 构造 SMTP 服务器地址
	smtpAddr := fmt.Sprintf("%s:%d", "smtp.qq.com", 465)

	// 发送邮件
	err := e.SendWithTLS(smtpAddr, smtp.PlainAuth("",
		"1690544550@qq.com",
		"znxogkkkexpbeeaj",
		"smtp.qq.com",
	), tlsConfig)
	if err != nil {
		log.Println("Failed to send email: ", err)
		return nil
	}

	return nil
}

// 获取用户导航文件
func getUserNavigationFile(c *gin.Context) {
	email := c.Param("email")
	if email == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"})
		return
	}

	// 获取密码参数
	password := c.Query("password")
	if password == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Password is required"})
		return
	}

	// 验证用户密码（从MongoDB查询）
	isValid, err := verifyUserPassword(email, password)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to verify password", "detail": err.Error()})
		return
	}
	if !isValid {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
		return
	}

	// 构建文件路径
	filename := email + "_navigation.json"
	filePath := filepath.Join(dataDir, filename)

	// 检查文件是否存在
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		c.JSON(http.StatusNotFound, gin.H{"error": "Navigation file not found"})
		return
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check file existence", "detail": err.Error()})
		return
	}

	// 读取文件内容
	file, err := os.Open(filePath)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file", "detail": err.Error()})
		return
	}
	defer file.Close()

	// 设置响应头
	c.Header("Content-Disposition", "attachment; filename="+filename)
	c.Header("Content-Type", "application/json")

	// 发送文件内容
	_, err = io.Copy(c.Writer, file)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send file", "detail": err.Error()})
		return
	}
}

// 验证用户密码（从MongoDB查询）
func verifyUserPassword(email, password string) (bool, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	client, err := connectMongoDB()
	if err != nil {
		return false, err
	}
	defer client.Disconnect(ctx)

	collection := client.Database(envMongoDBName).Collection("users")

	var user UserDocument
	err = collection.FindOne(ctx, bson.M{"email": email}).Decode(&user)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return false, nil
		}
		return false, err
	}

	// todo: 生产环境应使用密码哈希验证，此处仅为示例
	return user.Password == password, nil
}
